/*
 * @Author: your name
 * @Date: 2021-04-09 16:57:11
 * @LastEditTime: 2021-04-10 12:03:49
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \TypeScript\18-类型保护.ts
 */
// 类型保护与区分类型
// 联合类型时候于哪些可以为不同类型的情况
function getSmallPet() {
    return {
        layEggs: function () { },
        swim: function () { },
        fly: function () { }
    };
}
var pet = getSmallPet();
// 每一个成员访问都会报错
// if (pet.swim) {
//     pet.swim();
// } else if (pet.fly) {
//     pet.fly
// }
// 使用类型断言
var pet2 = getSmallPet();
if (pet.swim) {
    pet.swim();
}
else {
    pet.fly();
}
// 用户自定义的类型保护
// 上面的代码中，为了确保能够正常判断，我们用了很多次类型断言，这种方法泰国麻烦了
// 假若我们一旦检查过类型，就嫩在之后的每个分支里清楚的知道pet的类型就好了
// 在TypeScript里的类型保护机制让他成为了现实。类型保护就是一些表达式，
// 他们会在运行时检查以确保在某个作用域里的类型
// 要定义一个类型保护， 我们只要简单的定义一个函数，他的返回值时一个 类型谓词：
// 在下个例子里， pet is Fish就是类型谓词。谓词为parameterName is Type 这种形式，
// parameterName必须是来自于当前函数签名里的一个参数名
var pet3 = getSmallPet();
function isFish(pet) {
    return pet.swim !== undefined;
}
// 每当使用一些变量调用 isFish时，
// TypeScript会将变量缩减为那个具体的类型，
// 只要这个类型与变量的原始i类型是兼容的
// ‘swim’ 和 ‘fly’ 调用都没有问题
if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}
// type类型保护
// 幸运的是，现在我们不必将typeof x === 'numbe'抽象成一个函数，
// 因为TypeScript可以将它识别为一个类型保护。
// 也就是说我们可以直接在代码里抽查类型了
function isNumber(x) {
    return typeof x === 'number';
}
function isString(x) {
    return typeof x === 'string';
}
function padLeft2(value, padding) {
    if (isNumber(padding)) {
        return Array(padding + 1).join(" ") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error("Expection string or number, got '" + padding + "'.");
}
// => 优化
function padLeft3(value, padding) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error("Excepted string or number, got '" + padding + "'");
}
var SpaceRepeatingPadder = /** @class */ (function () {
    function SpaceRepeatingPadder(numSpaces) {
        this.numSpaces = numSpaces;
    }
    SpaceRepeatingPadder.prototype.getPaddingString = function () {
        return Array(this.numSpaces + 1).join(" ");
    };
    return SpaceRepeatingPadder;
}());
var StringPadding = /** @class */ (function () {
    function StringPadding(value) {
        this.value = value;
    }
    StringPadding.prototype.getPaddingString = function () {
        return this.value;
    };
    return StringPadding;
}());
function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadding(" ");
}
// 类型为SpaceRepeatingPadder | StringPadder
var padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
    // 类型细化为SpaceRepeatingPadder
    padder;
}
if (padder instanceof StringPadding) {
    // 类型细化为StringPadder
    padder;
}
// 可以为null的类型之可选参数
// 似乎用了 --strictNullChecks,可选参数会被Zion给地加上 | undefined
function f(x, y) {
    return x + (y || 0);
}
f(1, 2);
f(1);
f(1, undefined);
f(1, null);
// 可选属性也会有同样的处理
var C = /** @class */ (function () {
    function C() {
    }
    return C;
}());
var c = new C();
c.a = 12;
c.a = undefined;
c.b = 13;
c.b = undefined;
c.b = null;
function test(x, y) {
}
test(1, null);
// 可以为null的类型之类型保护和类型断言
// 由于可以为null的类型是通过联合类型实现
// 那么你需要使用理想保护来去除nll
// 很明显的去除了null
function f2(sn) {
    if (sn == null) {
        return "default";
    }
    else {
        return sn;
    }
}
// 也可以使用短路运算符
function f3(sn) {
    return sn || "default";
}
// 可以为null的类型值类型保护和类型断言
// 如果编译器不能够去除null或undefined， 
// 你可以使用类型断言手动去除
// 语法是添加!后缀: identifier!从identifier的类型里去除了null和undefined:
function fixed(name) {
    function postfix(epithet) {
        return name.charAt(0) + '. the' + epithet;
    }
    name = name || "Bob";
    return postfix('great');
}
function getName(n) {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}
// 与交叉类型一起使用，我们可以常见出一些十分稀奇古怪的类型
var people;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
var s = people.next.next.next.next.name;
var s = people.next.next.next.next.next.name;
